package handle

import (
	"MPCBot/global"
	"MPCBot/handle/contract"
	"MPCBot/model"
	"MPCBot/utils"
	"MPCBot/utils/config"
	"MPCBot/utils/convert"
	"MPCBot/utils/ether"
	"bytes"
	"context"
	"encoding/hex"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"math/big"
	"net/http"
	"net/url"
	"strconv"
	"strings"
	"time"

	"github.com/ethereum/go-ethereum"
	"github.com/ethereum/go-ethereum/accounts/abi"
	"github.com/ethereum/go-ethereum/common"
	"github.com/ethereum/go-ethereum/common/hexutil"
	"github.com/ethereum/go-ethereum/core/types"
	"github.com/ethereum/go-ethereum/crypto"
	"github.com/ethereum/go-ethereum/ethclient"
)

type HyOrderInfo struct {
	ID                  int
	Type                string
	Node                common.Address
	TronNode            string
	EParam              contract.IPayerExeTaskParam
	CParam              CosmosParam
	Data                []byte
	Signature           []byte
	SrcHash             string
	DstHash             string
	SrcChainNetWorkInfo *config.NetWorkInfo
	DstChainNetWorkInfo *config.NetWorkInfo
	Client              *ethclient.Client
	signAccount         *model.Account
	execAccount         *model.Account
	InsertTime          time.Time
	IsTryUpdate         bool
	DelayGetMinute      float64
	Flag                int64
	CurrencyAmount      float64
	Receiver            string
	CurrencyCode        string
	TrToken             string
	TrTokenAmount       string
	TrTokenDeciaml      uint8
	TrTokenSymbol       string
	TxHash              string
	IsSwapEth           bool
	SwapSource          string
	SwapParams          string
}

type CosmosParam struct {
	Node            string
	AmountIn        string
	TokenIn         string
	AmountOut       string
	TokenOut        string
	Receiver        string
	PayTaskId       *big.Int
	SwapDstChainGas int
}

// Verify and init HyOrderInfo
func (_hyOrderInfo *HyOrderInfo) NewOrderInfo(hYOrder *model.HybridgeOrder) bool {
	var err error
	_hyOrderInfo.ID = int(hYOrder.ID)
	_hyOrderInfo.Type = hYOrder.Type
	_hyOrderInfo.InsertTime = hYOrder.InsertTime
	// init rpc client
	if !_hyOrderInfo.initRpcClient(hYOrder) {
		global.Log.Error("====>  orderID_[%v] NewHyOrderInfo InitRpcClient error ", hYOrder.ID)
		return false
	}
	if _hyOrderInfo.DstChainNetWorkInfo.ChainID == model.CosmosChainID {
		_hyOrderInfo.SrcHash = hYOrder.SrcHash
		_hyOrderInfo.CParam.Node = hYOrder.Node
		_hyOrderInfo.CParam.AmountIn = hYOrder.AmountIn
		_hyOrderInfo.CParam.AmountOut = hYOrder.AmountOut
		_hyOrderInfo.CParam.TokenIn = hYOrder.TokenIn
		_hyOrderInfo.CParam.TokenOut = hYOrder.TokenOut
		_hyOrderInfo.CParam.Receiver = hYOrder.Receiver
		_hyOrderInfo.CParam.SwapDstChainGas = convert.MustInt(hYOrder.SwapDstChainGas)

		payOrderID, ok := new(big.Int).SetString(hYOrder.PayorderID, 10)
		if !ok {
			global.Log.Error("====>  orderID_[%v] NewHyOrderInfo amountOut_[%v] value error", hYOrder.ID, hYOrder.PayorderID)
			return false
		} else {
			_hyOrderInfo.EParam.PayTaskId = payOrderID
			_hyOrderInfo.CParam.PayTaskId = payOrderID
		}

		return true
	}

	global.Log.Error("====>  orderID_[%v] NewHyOrderInfo use default node_[%v]", hYOrder.ID, _hyOrderInfo.DstChainNetWorkInfo.DefaultNode)
	_hyOrderInfo.Node = common.HexToAddress(_hyOrderInfo.DstChainNetWorkInfo.DefaultNode)

	if hYOrder.SrcHash != "" {
		_hyOrderInfo.SrcHash = hYOrder.SrcHash
	}

	// check eparam __________________________________begin
	{
		amountOut, ok := new(big.Int).SetString(hYOrder.AmountOut, 10)
		if !ok {
			global.Log.Error("====>  orderID_[%v] NewHyOrderInfo amountOut_[%v] value error", hYOrder.ID, hYOrder.AmountOut)
		}
		_hyOrderInfo.EParam.AmountOut = amountOut
		if common.IsHexAddress(hYOrder.TokenOut) {
			_hyOrderInfo.EParam.TokenOut = common.HexToAddress(hYOrder.TokenOut)
		} else {
			global.Log.Error("====>  orderID_[%v] NewHyOrderInfo convert TokenOut_[%v] to HexAddress error", hYOrder.ID, hYOrder.TokenOut)
			return false
		}
		if common.IsHexAddress(hYOrder.Receiver) {
			_hyOrderInfo.EParam.Receiver = common.HexToAddress(hYOrder.Receiver)
		} else {
			global.Log.Error("====>  orderID_[%v] NewHyOrderInfo convert Receiver_[%v] to HexAddress error", hYOrder.ID, hYOrder.Receiver)
			return false
		}
		payOrderID, ok := new(big.Int).SetString(hYOrder.PayorderID, 10)
		if !ok {
			global.Log.Error("====>  orderID_[%v] NewHyOrderInfo amountOut_[%v] value error", hYOrder.ID, hYOrder.PayorderID)
			return false
		} else {
			_hyOrderInfo.EParam.PayTaskId = payOrderID
		}
	}

	// check swapparams order __________________________________begin
	_hyOrderInfo.CurrencyCode = hYOrder.TokenIn
	_hyOrderInfo.Receiver = hYOrder.Receiver
	_hyOrderInfo.TrToken = hYOrder.TokenOut
	_hyOrderInfo.TrTokenAmount = hYOrder.AmountOut
	_hyOrderInfo.IsSwapEth = false
	_hyOrderInfo.SwapParams = ""
	_hyOrderInfo.CurrencyAmount, err = strconv.ParseFloat(hYOrder.AmountIn, 64)
	if err != nil {
		global.Log.Error("====>  orderID_[%v]  convert amountIn to float error: %v", hYOrder.ID, err.Error())
		return false
	}
	_hyOrderInfo.TrTokenDeciaml, _hyOrderInfo.TrTokenSymbol = ether.GetTokenInfo(_hyOrderInfo.Client, hYOrder.TokenOut)
	if !strings.EqualFold(_hyOrderInfo.TrTokenSymbol, "usdt") && !strings.EqualFold(_hyOrderInfo.TrTokenSymbol, "usdc") && !strings.Contains(_hyOrderInfo.TrTokenSymbol, "usdt") && !strings.Contains(_hyOrderInfo.TrTokenSymbol, "USDT") {
		if strings.EqualFold(_hyOrderInfo.TrToken, model.AddressZero) {
			_hyOrderInfo.IsSwapEth = true
			if _hyOrderInfo.DstChainNetWorkInfo.ChainID == 56 {
				_hyOrderInfo.TrToken = global.GetTokenInfoBySymbol(_hyOrderInfo.DstChainNetWorkInfo.ChainID, "WBNB").Address
			} else {
				_hyOrderInfo.TrToken = global.GetTokenInfoBySymbol(_hyOrderInfo.DstChainNetWorkInfo.ChainID, "WETH").Address
			}
		}
		if !_hyOrderInfo.makeSwapParams() {
			global.Log.Error("====>  orderID_[%v]  MakeSwapParams swapsource not find", hYOrder.ID)
			return false
		}
	}

	return true
}

func (_hyOrderInfo *HyOrderInfo) NewOrderInfoByOrderID(payOrderID string) (ok bool) {
	hyOrderInfoJson, err := global.RD.GetString(payOrderID)
	if err != nil || hyOrderInfoJson == "" {
		global.Log.Error("====>  redis GetString hyOrderInfoJson error： %s", err.Error())
		return
	}
	detRedisByKey(payOrderID)
	err = json.Unmarshal([]byte(hyOrderInfoJson), &_hyOrderInfo)
	if err != nil {
		global.Log.Error("====>  redis Unmarshal hyOrderInfoJson_[%v] error：%v", hyOrderInfoJson, err)
		return
	}
	global.Log.Info("====>  redis NewOrderInfoByJson hyOrderInfoJson_[%v] success", hyOrderInfoJson)
	if _hyOrderInfo.DstChainNetWorkInfo.ChainID != model.CosmosChainID {
		_hyOrderInfo.DstChainNetWorkInfo.RpcUrls, _hyOrderInfo.Client = ether.GetEtherClient(_hyOrderInfo.DstChainNetWorkInfo.RpcUrls)
	}

	return true
}

// Generate bot signature
func (_hyOrderInfo *HyOrderInfo) MakeSign(signAccount *model.Account) bool {
	if _hyOrderInfo.DstChainNetWorkInfo.ChainID == model.CosmosChainID {
		sucstr := "====>  make signature success: cosmos"
		fmt.Println(model.Yellow, sucstr, model.Reset)
		return _hyOrderInfo.saveRedis()
	}
	global.Log.Info("====>  orderID_[%v] MakeSign begin eparam[%v]", _hyOrderInfo.ID, _hyOrderInfo.EParam)
	_hyOrderInfo.signAccount = signAccount
	var prikey, _ = crypto.HexToECDSA(_hyOrderInfo.signAccount.PriKey)
	structEParam, _ := abi.NewType("tuple", "struct thing", []abi.ArgumentMarshaling{
		{Name: "AmountOut", Type: "uint256"},
		{Name: "TokenOut", Type: "address"},
		{Name: "Receiver", Type: "address"},
		{Name: "PayTaskId", Type: "uint256"},
	})
	args := abi.Arguments{
		{Type: structEParam, Name: "param_one"},
	}
	packed, err := args.Pack(&_hyOrderInfo.EParam)
	if err != nil {
		global.Log.Info("====>  orderID_[%v] MakeSign EParam args.Pack err：%v", _hyOrderInfo.ID, err.Error())
	} else {
		fmt.Println("abi encoded", hexutil.Encode(packed))
	}

	hash := crypto.Keccak256Hash(
		packed,
		crypto.Keccak256Hash(_hyOrderInfo.Data).Bytes(),
	)
	crysign, err := crypto.Sign(hash.Bytes(), prikey)
	if err != nil {
		global.Log.Error("====>  MakeSign make sign error:%v", err)
		return false
	}
	crysign[64] += 27
	crysigns := crysign
	signStr := hexutil.Encode(crysigns)

	global.Log.Info("====> digest :%v signature :%v", hash, signStr)
	sucstr := "====>  make signature success:" + signStr
	fmt.Println(model.Yellow, sucstr, model.Reset)

	_hyOrderInfo.Signature = common.FromHex(signStr)

	return _hyOrderInfo.saveRedis()
}

// call contract to exec order
func (_hyOrderInfo *HyOrderInfo) ExecuteOrder(execAccount *model.Account) (string, bool) {
	defer _hyOrderInfo.Client.Close()

	_hyOrderInfo.execAccount = execAccount
	if _hyOrderInfo.Type != "2" && !_hyOrderInfo.checkWorkeAdrLimit() {
		return "", false
	}

	global.Log.Info("====>  orderID_[%v] begin call contract to execute", _hyOrderInfo.ID)

	nonce, err := _hyOrderInfo.Client.NonceAt(context.Background(), common.HexToAddress(_hyOrderInfo.execAccount.Addr), nil)
	if err != nil {
		global.Log.Error("====>  orderID_[%v] get nonce: error：%v", _hyOrderInfo.ID, err.Error())
		return "", false
	}
	abiData, err := ioutil.ReadFile(global.Conf.RunParams.ABIInfoPath + "MPCNode.abi")
	if err != nil {
		global.Log.Error("====>  orderID_[%v] read abi file: %v", _hyOrderInfo.ID, err.Error())
		return "", false
	}
	contractABI, err := abi.JSON(bytes.NewReader(abiData))
	if err != nil {
		global.Log.Error("====>  orderID_[%v] abi json error: %v", _hyOrderInfo.ID, err.Error())
		return "", false
	}

	var prikey, _ = crypto.HexToECDSA(_hyOrderInfo.execAccount.PriKey)
	global.Log.Info("====>  orderID_[%v] abi pack: hyOrderInfo %v", _hyOrderInfo.ID, _hyOrderInfo)

	chainID, err := _hyOrderInfo.Client.ChainID(context.Background())
	if err != nil {
		global.Log.Error("====>  orderID_[%v] Transaction create error: %v", _hyOrderInfo.ID, err.Error())
		return "", false
	}
	if len(_hyOrderInfo.Signature) == 0 {
		global.Log.Error("====>  orderID_[%v] bot Signature error %v", _hyOrderInfo.ID, _hyOrderInfo.Signature)
		return "", false
	}

	global.Log.Info("====>  orderID_[%v] Node : %v", _hyOrderInfo.ID, _hyOrderInfo.Node)
	global.Log.Info("====>  orderID_[%v] bot _hyOrderInfo.EParam : %v", _hyOrderInfo.ID, _hyOrderInfo.EParam)
	global.Log.Info("====>  orderID_[%v] bot _hyOrderInfo.Data : %v", _hyOrderInfo.ID, _hyOrderInfo.Data)
	callData, err := contractABI.Pack("executeTask", _hyOrderInfo.EParam, common.FromHex(_hyOrderInfo.SwapParams), _hyOrderInfo.Data, _hyOrderInfo.Signature)

	if err != nil {
		global.Log.Error("====>  orderID_[%v] abi pack: error: %v", _hyOrderInfo.ID, err.Error())
		return "", false
	}
	// estimated gas price and gas limit
	gasPrice, err := _hyOrderInfo.Client.SuggestGasPrice(context.Background())
	if err != nil {
		global.Log.Error("====>  orderID_[%v] gas price error：%v", _hyOrderInfo.ID, err.Error())
		return "", false
	}
	gasPrice1 := gasPrice.Mul(gasPrice, big.NewInt(3))
	msg := ethereum.CallMsg{
		From:     common.HexToAddress(_hyOrderInfo.execAccount.Addr),
		To:       &_hyOrderInfo.Node,
		Gas:      0,
		GasPrice: gasPrice1,
		Value:    big.NewInt(0),
		Data:     callData,
	}
	estimatedGas, err := _hyOrderInfo.Client.EstimateGas(context.Background(), msg)
	if err != nil {
		global.Log.Error("====>  orderID_[%v] estimatedGas error：%v", _hyOrderInfo.ID, err.Error())
		return "", false
	}
	//create tr
	tx := types.NewTransaction(nonce, _hyOrderInfo.Node, big.NewInt(0), estimatedGas, gasPrice1, callData)
	// sign tx
	signedTx, err := types.SignTx(tx, types.NewEIP155Signer(chainID), prikey)
	if err != nil {
		global.Log.Error("====>  orderID_[%v] sign error: %v", _hyOrderInfo.ID, err.Error())
		return "", false
	}
	// send tx
	err = _hyOrderInfo.Client.SendTransaction(context.Background(), signedTx)
	if err != nil {
		global.Log.Error("====>  orderID_[%v] send tx error: %v", _hyOrderInfo.ID, err.Error())
		return "", false

	} else {
		trHash := signedTx.Hash().Hex()
		_hyOrderInfo.DstHash = trHash
		_hyOrderInfo.TxHash = trHash
		_hyOrderInfo.UpdateOrderStatus(_hyOrderInfo.EParam.PayTaskId.String(), model.Execing, trHash)
		time.Sleep(_hyOrderInfo.DstChainNetWorkInfo.TrComfirmTime * time.Second)
		receipt, err := _hyOrderInfo.Client.TransactionReceipt(context.Background(), signedTx.Hash())
		global.Log.Info("====>  orderID_[%v] TransactionReceipt %v", _hyOrderInfo.ID, receipt)
		if err != nil {
			global.Log.Error("====>  orderID_[%v] client.TransactionReceipt error: %v trHasx [%v]", _hyOrderInfo.ID, err.Error(), trHash)
			fmt.Println(model.Red, "====>  ten second later try get receipt again ......", model.Reset)
			time.Sleep(_hyOrderInfo.DstChainNetWorkInfo.TrComfirmTime * time.Second)
			receipt, err = _hyOrderInfo.Client.TransactionReceipt(context.Background(), signedTx.Hash())
			if err != nil {
				global.Log.Error("====>  orderID_[%v] client.TransactionReceipt error: %v trHasx [%v]", _hyOrderInfo.ID, err.Error(), trHash)
				return trHash, false
			}
		}
		if receipt.Status != 1 {
			errstr := "====>  orderID_[" + strconv.Itoa(_hyOrderInfo.ID) + "] client.TransactionReceipt error trHasx [" + trHash + "]"
			fmt.Println(model.Red, errstr, model.Reset)
			return trHash, false
		}
		global.Log.Info("====>  orderID_[%v] contract exec order success!", _hyOrderInfo.ID)
		hashstr := "====>  contract exec order success! orderid_[" + strconv.Itoa(_hyOrderInfo.ID) + "] workeradr [" + _hyOrderInfo.execAccount.Addr + "] trade hash: [" + trHash + "]"
		fmt.Println(model.Yellow, hashstr, model.Reset)

		return trHash, true
	}
}

// update HyOrder state and trhash
func (_hyOrderInfo *HyOrderInfo) UpdateOrderStatus(payOrderID string, state string, trHash string) bool {
	payOrderID = strings.Replace(payOrderID, model.RDPrefixEvmOrder, "", -1)
	global.Log.Info("====>  orderID_[%v] update order state", _hyOrderInfo.ID)
	apiUrl := global.Conf.RunParams.TaskApi + "api/crosschain/updatehytask"
	// timeStamp := strconv.Itoa(int(time.Now().Unix()))
	// sign, _ := MakeAPISign("updatetask timestamp:"+timeStamp, robotAccountList[0])
	// if !ok {
	// 	global.Log.Error("UpdateOrderStatus makeAPISign error")
	// 	return false
	// }

	global.Log.Warn("UpdateOrderStatus [%v]  state[%v] trHash[%v]", payOrderID, state, trHash)
	resp, err := http.PostForm(apiUrl, url.Values{"taskid": {payOrderID}, "state": {state}, "dsthash": {trHash}})
	if err != nil {
		global.Log.Error("UpdateOrderStatus makeAPISign error")
		return false
	}

	defer resp.Body.Close()
	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		global.Log.Error("UpdateOrderStatus makeAPISign error")
		return false
	}
	global.Log.Info("====> UpdateOrderStatus resposeJson [%s]", body)
	if err != nil {
		global.Log.Error("UpdateOrderStatus error：%v", err.Error())
		return false
	}
	var apiResult model.ApiResult
	if err := json.Unmarshal([]byte(body), &apiResult); err != nil {
		global.Log.Error("UpdateHyOrderStatus error：%v", err.Error())
		return false
	}
	return apiResult.Ret == 200
}

func (_hyOrderInfo *HyOrderInfo) initRpcClient(hYOrder *model.HybridgeOrder) bool {
	srcChainId, dstChainId, _, flag := GetSrcDstChainIDAndExpTime(hYOrder.PayorderID)
	// if expTime < time.Now().Unix() {
	// 	global.Log.Error("====>  orderID_[%v] execute time_[%v] Expired", hYOrder.ID, expTime)
	// 	return false
	// }
	_hyOrderInfo.Flag = flag
	_hyOrderInfo.SrcChainNetWorkInfo = global.GetNewWorkInfo(int(srcChainId))
	_hyOrderInfo.DstChainNetWorkInfo = global.GetNewWorkInfo(int(dstChainId))
	if _hyOrderInfo.SrcChainNetWorkInfo.ChainID == 0 || _hyOrderInfo.DstChainNetWorkInfo.ChainID == 0 {
		global.Log.Error("====>  orderID_[%v] GetNewWorkInfo not support network srcChainId_[%v] dstChainId_[%v]", hYOrder.ID, srcChainId, dstChainId)
		return false
	}
	if _hyOrderInfo.DstChainNetWorkInfo.ChainID == model.CosmosChainID {
		return true
	}
	_hyOrderInfo.DstChainNetWorkInfo.RpcUrls, _hyOrderInfo.Client = ether.GetEtherClient(_hyOrderInfo.DstChainNetWorkInfo.RpcUrls)
	if len(_hyOrderInfo.SrcChainNetWorkInfo.RpcUrls) == 0 {
		global.InitNetWorkInfo()
		_hyOrderInfo.DstChainNetWorkInfo.RpcUrls, _hyOrderInfo.Client = ether.GetEtherClient(_hyOrderInfo.DstChainNetWorkInfo.RpcUrls)
		if len(_hyOrderInfo.DstChainNetWorkInfo.RpcUrls) == 0 {
			return false
		}
		global.Log.Info("====>  orderID_[%v] InitRpcClient  srcChainId_[%v] dstChainId_[%v]", hYOrder.ID, srcChainId, dstChainId)
	}

	return true
}

func (_hyOrderInfo *HyOrderInfo) makeSwapParams() (ok bool) {
	global.Log.Info("====>  orderID_[%v] MakeSwapParams encodePacked: ", _hyOrderInfo.ID)
	forEth := 0
	if _hyOrderInfo.IsSwapEth {
		forEth = 1
	}
	tokenIn := global.GetTokenInfoBySymbol(_hyOrderInfo.DstChainNetWorkInfo.ChainID, "usdt").Address
	tokenInDeciaml := ether.GetTokenDeciaml(_hyOrderInfo.Client, tokenIn)
	amountIn := ether.AppendWei(int(_hyOrderInfo.CurrencyAmount*10000), int(tokenInDeciaml-4))
	swapSource := getSwapSource(_hyOrderInfo.TrToken)
	if swapSource == model.Encentive {
		_hyOrderInfo.SwapParams = hex.EncodeToString(ether.EncodePacked(
			common.LeftPadBytes(common.HexToAddress(tokenIn).Bytes(), 20),
			common.LeftPadBytes(amountIn.Bytes(), 11),
			common.LeftPadBytes(big.NewInt(1).Bytes(), 1),
			common.LeftPadBytes(common.HexToAddress(_hyOrderInfo.TrToken).Bytes(), 32),
			common.LeftPadBytes(big.NewInt(int64(forEth)).Bytes(), 32),
			common.LeftPadBytes(big.NewInt(0).Bytes(), 32),
		))
		global.Log.Info("====>  orderID_[%v] getSwapParams swapparams_[%v]", _hyOrderInfo.ID,
			hex.EncodeToString(ether.EncodePacked(
				common.LeftPadBytes(common.HexToAddress(tokenIn).Bytes(), 20),
				common.LeftPadBytes(amountIn.Bytes(), 11),
				common.LeftPadBytes(big.NewInt(1).Bytes(), 1),
				common.LeftPadBytes(common.HexToAddress(_hyOrderInfo.TrToken).Bytes(), 32),
				common.LeftPadBytes(big.NewInt(int64(forEth)).Bytes(), 32),
				common.LeftPadBytes(big.NewInt(0).Bytes(), 32),
			)))
		ok = true
	}
	if swapSource == model.Pancake {
		swapPath := "0"
		if swapTokenPath := global.GetSwapTokenPath(_hyOrderInfo.DstChainNetWorkInfo.ChainID, _hyOrderInfo.TrToken); swapTokenPath != nil {
			swapPath = swapTokenPath.SwapPath
		}
		_hyOrderInfo.SwapParams = hex.EncodeToString(ether.EncodePacked(
			common.LeftPadBytes(common.HexToAddress(tokenIn).Bytes(), 20),
			common.LeftPadBytes(amountIn.Bytes(), 11),
			common.LeftPadBytes(big.NewInt(2).Bytes(), 1),
			common.LeftPadBytes(common.HexToAddress(swapPath).Bytes(), 32),
			common.LeftPadBytes(common.HexToAddress(_hyOrderInfo.TrToken).Bytes(), 32),
			common.LeftPadBytes(big.NewInt(0).Bytes(), 32),
			common.LeftPadBytes(big.NewInt(int64(forEth)).Bytes(), 32),
		))
		global.Log.Info("====>  orderID_[%v] getSwapParams swapparams_[%v]", _hyOrderInfo.ID,
			hex.EncodeToString(ether.EncodePacked(
				common.LeftPadBytes(common.HexToAddress(tokenIn).Bytes(), 20),
				common.LeftPadBytes(amountIn.Bytes(), 11),
				common.LeftPadBytes(big.NewInt(2).Bytes(), 1),
				common.LeftPadBytes(common.HexToAddress(swapPath).Bytes(), 32),
				common.LeftPadBytes(common.HexToAddress(_hyOrderInfo.TrToken).Bytes(), 32),
				common.LeftPadBytes(big.NewInt(0).Bytes(), 32),
				common.LeftPadBytes(big.NewInt(int64(forEth)).Bytes(), 32),
			)))
		ok = true
	}
	return
}

func (_hyOrderInfo *HyOrderInfo) saveRedis() (ok bool) {
	key := model.RDPrefixEvmOrder + _hyOrderInfo.EParam.PayTaskId.String()
	hyOrderInfoJson, err := global.RD.GetString(key)
	if err != nil {
		global.Log.Error("====>  redis GetString hyOrderInfoJson error： %s", err.Error())
		// return
	}
	if hyOrderInfoJson == "" {
		hyOrderInfoJson, err := json.Marshal(_hyOrderInfo)
		if err != nil {
			global.Log.Error("====>  redis json.Marshal _hyOrderInfo_[%v] error：%v", _hyOrderInfo, err)
			return
		}
		if _, err := global.RD.SetString(key, hyOrderInfoJson); err != nil {
			global.Log.Error("====>  redis SetString key_[%v] hyOrderInfoJson_[%v] error：%v", key, hyOrderInfoJson, err)
			return
		}
	}
	global.Log.Info("====>  redis GetString hyOrderInfoJson_[%v] success", hyOrderInfoJson)

	return true
}

func (_hyOrderInfo *HyOrderInfo) CheckChainSrcOrder() bool {

	if _hyOrderInfo.SrcChainNetWorkInfo.ChainID == model.CosmosChainID {
		return true
	}
	var wsClient *ethclient.Client
	_hyOrderInfo.SrcChainNetWorkInfo.RpcUrls, wsClient = ether.GetEtherClient(_hyOrderInfo.SrcChainNetWorkInfo.RpcUrls)
	if len(_hyOrderInfo.SrcChainNetWorkInfo.RpcUrls) == 0 {
		global.InitNetWorkInfo()
		_hyOrderInfo.SrcChainNetWorkInfo.RpcUrls, wsClient = ether.GetEtherClient(_hyOrderInfo.SrcChainNetWorkInfo.RpcUrls)
		if len(_hyOrderInfo.SrcChainNetWorkInfo.RpcUrls) == 0 {
			return false
		}
	}

	defer wsClient.Close()

	tokenOutContract, err := contract.NewToken(_hyOrderInfo.EParam.TokenOut, _hyOrderInfo.Client)
	if err != nil {
		global.Log.Error("====>  orderID_[%v] CheckChainSrcOrder tokenOut_[%v] error: %v", _hyOrderInfo.ID, _hyOrderInfo.EParam.TokenOut, err.Error())
		return false
	}

	abiData, err := ioutil.ReadFile(global.Conf.RunParams.ABIInfoPath + "Payer.abi")
	if err != nil {
		global.Log.Error("====>  orderID_[%v] CheckChainSrcOrder read abi file: %v", _hyOrderInfo.ID, err.Error())
		return false
	}
	contractABI, err := abi.JSON(bytes.NewReader(abiData))
	if err != nil {
		global.Log.Error("====>  orderID_[%v] CheckChainSrcOrder abi json error: %v", _hyOrderInfo.ID, err.Error())
		return false
	}

	// filter log at srcHash events
	var logs []types.Log
	payDBAddress := common.HexToAddress(_hyOrderInfo.SrcChainNetWorkInfo.PayDB)
	payDBContract, err := contract.NewPayer(payDBAddress, _hyOrderInfo.Client)
	srcHash := common.HexToHash(_hyOrderInfo.SrcHash)
	receipt, err := wsClient.TransactionReceipt(context.Background(), srcHash)
	if err != nil {
		global.Log.Error("====>  orderID_[%v] CheckChainSrcOrder: TransactionReceipt error：%v", _hyOrderInfo.ID, err.Error())
		_hyOrderInfo.DelayGetMinute = 3
		return false
	}
	if receipt.Status != 1 {
		global.Log.Error("====>  orderID_[%v] CheckChainSrcOrder receipt.Status ：%v", _hyOrderInfo.ID, receipt.Status)
		return false
	}
	for lindex, vLog := range receipt.Logs {
		if receipt.Logs[lindex].Topics[0] == contractABI.Events["TaskCreated"].ID {
			receiptEvent, err := payDBContract.ParseTaskCreated(*vLog)
			if err != nil {
				global.Log.Error("====>  orderID_[%v] CheckChainSrcOrder ParseTaskCreated error: %v", _hyOrderInfo.ID, err.Error())
				return false
			}
			if receiptEvent.PayTaskId.String() == _hyOrderInfo.EParam.PayTaskId.String() {
				global.Log.Error("====>  orderID_[%v] CheckChainSrcOrder find event payorderid_[%v]", _hyOrderInfo.ID, receiptEvent.PayTaskId.String())
				logs = append(logs, *vLog)
			}
		}
	}

	filterSuccess := false
	for _, vLog := range logs {
		orderCreatedEvent, err := payDBContract.ParseTaskCreated(vLog)
		if err != nil {
			global.Log.Error("====>  orderID_[%v] CheckChainSrcOrder ParseTaskCreated error: %v", _hyOrderInfo.ID, err.Error())
			return false
		}

		global.Log.Info("====>  orderID_[%v] find srcchain event Log: %v", _hyOrderInfo.ID, vLog)
		amountInEvent := orderCreatedEvent.AmountIn
		tokenInEvent := orderCreatedEvent.TokenIn
		global.Log.Info("====>  orderID_[%v] amountInEvent_[%v] tokenInEvent_[%v] %v", _hyOrderInfo.ID, amountInEvent, tokenInEvent)
		tokenInInfo := global.GetTokenInfo(_hyOrderInfo.SrcChainNetWorkInfo.ChainID, tokenInEvent.String())
		if tokenInInfo == nil {
			global.Log.Error("====>  OrderCreated event Log tokenIn address error: ChainName_[" + _hyOrderInfo.SrcChainNetWorkInfo.Name + "] Node_[" + _hyOrderInfo.SrcChainNetWorkInfo.DefaultNode + "] payOrderId_[" + _hyOrderInfo.EParam.PayTaskId.String() + "] tokenIn_[" + tokenInEvent.String() + "]")
			noticeMsg := " ChainName_[" + _hyOrderInfo.SrcChainNetWorkInfo.Name + "] Node_[" + _hyOrderInfo.SrcChainNetWorkInfo.DefaultNode + "] payOrderId_[" + _hyOrderInfo.EParam.PayTaskId.String() + "] \n"
			noticeMsg += "detail: tokenIn_[" + tokenInEvent.String() + "] \n  amountIn_[" + amountInEvent.String() + "] \n trhash_[" + vLog.TxHash.Hex() + "] "
			fmt.Println(model.Red, noticeMsg, model.Reset)
			utils.SendNotice(noticeMsg, "OrderCreated event Log tokenIn address error!")
			return false
		}
		global.Log.Info("====>  orderID_[%v] check amountOut", _hyOrderInfo.ID)
		if _hyOrderInfo.DstChainNetWorkInfo.ChainID == model.CosmosChainID {
			return true
		}
		amountOutEvent := orderCreatedEvent.AmountOut
		if _hyOrderInfo.EParam.AmountOut.Cmp(amountOutEvent) != 0 {
			global.Log.Error("====>  orderID_[%v] CheckChainSrcOrder EParam.AmountOut_[%v] no equal to amountOutEvent_[%v] ", _hyOrderInfo.ID, _hyOrderInfo.EParam.AmountOut, amountOutEvent)
			return false
		}
		global.Log.Info("====>  orderID_[%v] check tokenOut", _hyOrderInfo.ID)
		tokenOutEvent := orderCreatedEvent.TokenOut
		if _hyOrderInfo.EParam.TokenOut != tokenOutEvent {
			global.Log.Error("====>  orderID_[%v] CheckChainSrcOrder EParam.TokenOut_[%v] no equal to tokenOutEvent_[%v] ", _hyOrderInfo.ID, _hyOrderInfo.EParam.TokenOut, tokenOutEvent)
			return false
		}
		tokenOutInfo := global.GetTokenInfo(_hyOrderInfo.DstChainNetWorkInfo.ChainID, tokenOutEvent.String())
		if tokenOutInfo == nil {
			global.Log.Error("====>  OrderCreated event Log tokenOut address error: ChainName_[" + _hyOrderInfo.DstChainNetWorkInfo.Name + "] Node_[" + _hyOrderInfo.DstChainNetWorkInfo.DefaultNode + "] payOrderId_[" + _hyOrderInfo.EParam.PayTaskId.String() + "] tokenOut_[" + tokenOutEvent.String() + "]")
			noticeMsg := " ChainName_[" + _hyOrderInfo.DstChainNetWorkInfo.Name + "] Node_[" + _hyOrderInfo.DstChainNetWorkInfo.DefaultNode + "] payOrderId_[" + _hyOrderInfo.EParam.PayTaskId.String() + "] \n"
			noticeMsg += "detail: tokenOut_[" + tokenOutEvent.String() + "] \n  amountOut_[" + amountOutEvent.String() + "] \n ] "
			fmt.Println(model.Red, noticeMsg, model.Reset)
			utils.SendNotice(noticeMsg, "OrderCreated event Log tokenOut address error!")
			return false
		}
		receiverEvent := orderCreatedEvent.Receiver
		if _hyOrderInfo.EParam.Receiver != receiverEvent {
			global.Log.Error("====>  OrderCreated event Log receiver address error: EParam.Receiver_[" + _hyOrderInfo.EParam.Receiver.String() + "]  receiverEvent_[" + receiverEvent.String() + "]")
			noticeMsg := " ChainName_[" + _hyOrderInfo.DstChainNetWorkInfo.Name + "] Node_[" + _hyOrderInfo.DstChainNetWorkInfo.DefaultNode + "] payOrderId_[" + _hyOrderInfo.EParam.PayTaskId.String() + "] \n"
			noticeMsg += "detail: EParam.Receiver_[" + _hyOrderInfo.EParam.Receiver.String() + "] \n  receiverEvent_[" + receiverEvent.String() + "] \n "
			fmt.Println(model.Red, noticeMsg, model.Reset)
			utils.SendNotice(noticeMsg, "OrderCreated event Log receiver address error!")
			return false
		}

		global.Log.Info("====>  orderID_[%v] check srcchain assets and dstchain assets volatility")
		// tokenInAssets := ether.GetTokenPrice(strings.ToLower(tokenInInfo.Symbol)) * ether.BigintToFloat64ByDecimal(amountInEvent, uint8(tokenInInfo.Decimals))
		// tokenOutAssets := ether.GetTokenPrice(strings.ToLower(tokenOutInfo.Symbol)) * ether.BigintToFloat64ByDecimal(amountOutEvent, uint8(tokenOutInfo.Decimals))
		// if math.Abs(tokenInAssets-tokenOutAssets)/tokenInAssets*100 > 10 {
		// 	global.Log.Error("====>  orderID_[%v] CheckChainSrcOrder tokenInAssets_[%v] tokenOutAssets_[%v] volatility too high", _hyOrderInfo.ID, tokenInAssets, tokenOutAssets)
		// 	return false
		// }
		global.Log.Info("====>  orderID_[%v]  check node balance")
		tokenOutNodeBalance, err := tokenOutContract.BalanceOf(nil, common.HexToAddress(_hyOrderInfo.DstChainNetWorkInfo.DefaultNode))
		if err != nil {
			global.Log.Error("====>  orderID_[%v] CheckChainSrcOrder get node_[%v] tokenOut_[%v] balance error: %v", _hyOrderInfo.ID, _hyOrderInfo.DstChainNetWorkInfo.DefaultNode, _hyOrderInfo.EParam.TokenOut, err.Error())
			return false
		}
		if tokenOutNodeBalance.Cmp(amountOutEvent) < 0 {
			global.Log.Error("====>  orderID_[%v] CheckChainSrcOrder node_[%v] tokenOut_[%v] balance_[%v] is not enough amountOut[%v]", _hyOrderInfo.ID, _hyOrderInfo.DstChainNetWorkInfo.DefaultNode, _hyOrderInfo.EParam.TokenOut, tokenOutNodeBalance.String(), amountOutEvent)
			// record balance node assets channel
			_, err = global.RD.SetString(model.NeedBalanceAssetsChain, _hyOrderInfo.DstChainNetWorkInfo.ChainID)
			if err != nil {
				global.Log.Error("redis SetString NeedBalanceAssetsChain error： %s", err.Error())
			}
			noticeMsg := "node_[" + _hyOrderInfo.DstChainNetWorkInfo.DefaultNode + "] token [" + tokenOutEvent.String() + "] balance [" + amountOutEvent.String() + "] is not enough"
			utils.SendNotice(noticeMsg, "node balance not enough!")
			return false
		}
		filterSuccess = true
		break
	}

	time.Sleep(1 * time.Second)
	if !filterSuccess {
		time.Sleep(1 * time.Second)
		_hyOrderInfo.DelayGetMinute = 3
	}

	return filterSuccess
}

// check order dailyAmount and maxDailyAmount
func (_hyOrderInfo *HyOrderInfo) checkWorkeAdrLimit() bool {
	// call contract to get this worker address dailyAmount and maxDailyAmount
	global.Log.Info("====> call contract to get worker address [%v] dailyAmount and maxDailyAmount", _hyOrderInfo.execAccount.Addr)

	workeAddress := common.HexToAddress(_hyOrderInfo.execAccount.Addr)
	var wokerRecord model.WokerRecord
	MPCNodeContract, err := contract.NewMPCNode(common.HexToAddress(_hyOrderInfo.DstChainNetWorkInfo.DefaultNode), _hyOrderInfo.Client)
	if err != nil {
		global.Log.Error("checkWorkeAdrLimit: error：%v", err.Error())
		return false
	}
	wokerRecord.UseRecord, err = MPCNodeContract.Records(nil, workeAddress, _hyOrderInfo.EParam.TokenOut)
	if err != nil {
		global.Log.Error("checkWorkeAdrLimit: error：%v", err.Error())
		return false
	}
	global.Log.Info("====>  worker address [%v] UseRecord_[%v]", _hyOrderInfo.execAccount.Addr, wokerRecord.UseRecord)
	wokerRecord.OperatorTokenAuth, err = MPCNodeContract.TokenAuth(nil, workeAddress, _hyOrderInfo.EParam.TokenOut)
	if err != nil {
		global.Log.Error("checkWorkeAdrLimit: error：%v", err.Error())
		return false
	}

	AmountOut := big.NewInt(0).Add(big.NewInt(0), _hyOrderInfo.EParam.AmountOut)
	dailyAmount := ether.BigintToFloat64ByDecimal(wokerRecord.OperatorTokenAuth.MaxDailyAmount, _hyOrderInfo.TrTokenDeciaml)
	dailyAmountUsed := ether.BigintToFloat64ByDecimal(wokerRecord.UseRecord.DailyAmountUsed, _hyOrderInfo.TrTokenDeciaml)
	maxAmountPerTx := ether.BigintToFloat64ByDecimal(wokerRecord.OperatorTokenAuth.MaxAmountPerTx, _hyOrderInfo.TrTokenDeciaml)
	trAmount := ether.BigintToFloat64ByDecimal(AmountOut, _hyOrderInfo.TrTokenDeciaml)
	global.Log.Info("====>  worker address [%v] MaxDailyAmount_[%v]", _hyOrderInfo.execAccount.Addr, wokerRecord.OperatorTokenAuth.MaxDailyAmount)
	global.Log.Info("====>  worker address [%v] DailyAmountUsed [%v]", _hyOrderInfo.execAccount.Addr, dailyAmountUsed)
	global.Log.Info("====>  worker address [%v] MaxAmountPerTx_[%v]", _hyOrderInfo.execAccount.Addr, wokerRecord.OperatorTokenAuth.MaxAmountPerTx)
	global.Log.Info("====>  order transaction amount [%v]", trAmount)

	if trAmount > maxAmountPerTx {
		// updateManualAuditDB(CurrencyPayInfo)
		utils.SendNotice("bot order audit limited, orderid:["+strconv.Itoa(_hyOrderInfo.ID)+"]; buy ["+_hyOrderInfo.TrTokenSymbol+"] amount:["+_hyOrderInfo.TrTokenAmount+"]; use wallet address["+_hyOrderInfo.Receiver+"]; please manual audit", "order audit limited report")
		global.Log.Error("====> bot order audit limited, maxAmountPerTx [%v] orderid:[%v] buy [%v]] amount:[%v];", fmt.Sprintf("%f", maxAmountPerTx), _hyOrderInfo.ID, _hyOrderInfo.TrTokenSymbol, trAmount)
		return false
	}
	if (dailyAmountUsed + trAmount) > dailyAmount {
		utils.SendNotice("bot worker account audit limited, workeAdr:["+_hyOrderInfo.execAccount.Addr+"]; dailyAmount ["+fmt.Sprintf("%f", dailyAmount)+"]; dailyAmountUsed:["+fmt.Sprintf("%f", dailyAmountUsed)+"];", "order audit limited report")
		global.Log.Error("====> bot order audit limited, workeAdr:[%v]; dailyAmount [%v] dailyAmountUsed:[%v];", _hyOrderInfo.execAccount.Addr, dailyAmount, dailyAmountUsed)
		return false
	}

	return true
}

func getSwapSource(tokenAdr string) string {
	return model.Pancake
}
